﻿using System;
using System.Collections.Generic;
using System.Drawing;
//using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
//using System.Windows;
using System.Windows.Media;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.Geometry.GridSupport;
using Petra.Gis.Drawing;
using Petra.Gis.RenderingServices.PlanarBase ;
using Petra.Gis.Geometry.Utils;
using Petra.Gis.ReferenceSystemServices;
using Petra.Gis.ReferenceSystemServices.Transformations;

namespace Petra.Gis.Drawing.GDIPlus
{
  /// <summary>
  /// renders point as 'dot' in map of defined size and color
  /// </summary>
  public partial class GridGridRenderer : GridRenderer
  {

    //protected Pen _outpen;

    public GridGridRenderer(MemGridMap map, Graphics g, DisplayTransformMatrix m, RenderStyle s)
      :base(map, g, m, s)
    {

      //parse style
      //Pen _outpen = new Pen(Color.FromArgb((int)_style["Color"]), (int)_style["PointSize"]);

    }




    /// <summary>
    /// method that implements drawing (conversion) of gis geometry into output format geometry
    /// </summary>
    /// <param name="geometry">any system type currently</param>
    public override void RenderGeometry(GeometryBase geometry)
    {
      //validate type against supported types
      if (geometry is Grid)
        renderGrid((Grid)geometry);
    }




    protected virtual void renderGrid(Grid g)
    {

      //temporary condition..
      if (!g.Transform.Source.EqualParams(g.Transform.Target, true))
        throw new NotImplementedException(@"sorry image transform between different 
                                            coordinate systems not supported now,
                                            only storage units transform is supported");

      // select frame from griddata
      // where pixel size is closest to desired map pixel size
      // see MapBase.Resolution
      // example: for MapBase.Resolution = 100 (ppi), 1px on screen = 0.000254m in map units
      //          0.000254m x MapBase.Scale = xx m
      double aproxMetersPerCell = (MapPaperUnits.inch.MetersPerUnit / (1.0 * _map.Resolution)) * (1.0 / _map.Scale);

      //select apropriate frame by pixelSize
      GridFrame f = GridMath.SelectGridFrameByPixelSize(g, aproxMetersPerCell);

      // if image supports blocks then select only blocks
      // which overlaps map extent
      // transform Bounds first
      
      if(f.Blocks.Length > 1)
        f = f.SelectBlocksByRange(g.InverseTransform.TransformBox(_map.Extent.Bounds));

      // use GridMath, process pixel transform
      // when source and target coordinate system differs
      if (!g.Transform.Source.EqualParams(g.Transform.Target, true))
        f = GridMath.TransformFrame(f, g.Transform);

      // display transform
      System.Drawing.Point outOrigin = _transform.TransformPoint(new PointD(f.Bounds.Left, f.Bounds.Top));
      System.Drawing.Point outSize = _transform.TransformPoint(new PointD(f.Bounds.Right, f.Bounds.Bottom));

      Rectangle outRect = new Rectangle(outOrigin.X, outOrigin.Y,
                                        outSize.X - outOrigin.X,
                                        outSize.Y - outOrigin.Y);

      //convert pixel format....
      System.Drawing.Imaging.PixelFormat p;

      if((f.PixelFormat.BitsPerPixel % 8) != 0)
        throw new ArgumentException(@"sorry, unsupported pixel format " + f.PixelFormat.ToString() +
                                     " must be modulo of 8");

      if (f.PixelFormat == PixelFormats.Indexed8)
        p = System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
      else if (f.PixelFormat == PixelFormats.Rgb24)
        p = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
      else
        throw new InvalidOperationException("unsupported GridFrame PixelFormat" + f.PixelFormat.ToString());

      // create temporary bitmap to hold selected image data
      Bitmap tmp = new Bitmap(f.Width, f.Height, p);

      // step over all blocks and draw its data into bitmap...
      int lastBlockPosX = 0, lastBlockPosY = 0;
      int size;
      System.Drawing.Imaging.BitmapData bd;
      GridBlock b;
      for(int y = 0; y < f.NumBlocksVertical; y++)
        for(int x = 0; x < f.NumBlocksHorizontal; x++)
        { 
          b = f.Blocks[(y * f.NumBlocksHorizontal) + x];
          bd = tmp.LockBits(new Rectangle(lastBlockPosX, lastBlockPosY, b.PixelWidth, b.PixelHeight),
                            System.Drawing.Imaging.ImageLockMode.ReadWrite, p);

          size = bd.Stride * bd.Height * (f.PixelFormat.BitsPerPixel / 8);

          b.Source.CopyPixels(new System.Windows.Int32Rect(lastBlockPosX, lastBlockPosY, b.PixelWidth, b.PixelHeight),
                              bd.Scan0, size, bd.Stride);
        }
      
      _outGraphic.DrawImage(tmp, outRect);
    }




    public override DomainGeometryType RendererGeometryDomain
    {
      get { return DomainGeometryType.Grid; }
    }




    public override bool IsRenderable(GeometryBase geometry)
    {
      if (geometry is Grid)
        return true;
      else
        return false;
    }




    protected override RenderStyle renderStyle
    {
      get
      {
        return this.Style;
      }
      set
      {
        this.Style = value;
      }
    }



    new public RenderStyle Style
    {
      get { return _style; }
      set
      {
        //todo: style must contain at least few items
        if (value == null)
          throw new ArgumentException("invalid null style", "style");

        _style = value;
      }
    }



  }
}
